1 Exemplo: altura e peso

Pessoas mais altas tendem a ser mais pesadas?

Temos dados de \(80\) alunos e alunas, com alturas em cm e pesos em kg:

2 Correlação linear

3 Covariância

3.1 Em R

cov(peso_altura$altura, peso_altura$peso)
## [1] 73,8935

3.2 Exercícios

  • Qual é a unidade da covariância? Examine as fórmulas acima para responder.

  • Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.

    k <- 10
    
    X <- 1:5
    Y <- k * X
    
    tibble(X, Y) %>% 
      ggplot(aes(X, Y)) +
        geom_point(size = 2) +
        labs(
          title = paste('cov(X, Y) =', cov(X, Y))
        )

  • Qual a covariância máxima entre duas variáveis? E a mínima?

  • Usando as propriedades do valor esperado, mostre que, para quaisquer variáveis aleatórias \(X\) e \(Y\)

    \[ \text{Cov}(X,Y) \quad=\quad E\big[(X - E(X)) \cdot (Y - E(Y))\big] \quad=\quad E(XY) - E(X)E(Y) \]

  • Lembre-se de que a variância de uma variável aleatória \(X\) pode ser escrita como

    \[ \text{Var}(X) = E(X^2) - [E(X)]^2 \]

    Alguma semelhança entre esta expressão e a expressão do item anterior?

  • Qual a covariância de uma variável aleatória \(X\) com ela mesma?

  • O R usa \(n - 1\) no denominador da covariância. Mostre que definir a covariância como \[ \text{Cov}(X, Y) = \frac{1}{n - 1} \cdot \sum_{i = 1}^n \big[(x_i - E(X)) \cdot (y_i - E(Y)) \big] \]

    é equivalente a

    \[ \text{Cov}(X, Y) = \frac{n}{n-1} \cdot \big[ E(XY) - E(X)E(Y) \big] \]

  • Usando o data frame peso_altura, compute o valor da expressão acima e compare com o resultado de cov(peso_altura$altura, peso_altura$peso).

4 Coeficiente de correlação linear

4.1 Em R

cor(peso_altura$peso, peso_altura$altura)
## [1] 0,6440311

4.2 Exercícios

  • Qual a unidade do coeficiente de correlação?

  • Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.

    k <- 10
    
    X <- 1:5
    Y <- k * X
    
    tibble(X, Y) %>% 
      ggplot(aes(X, Y)) +
        geom_point(size = 2) +
        labs(
          title = paste('cor(X, Y) =', cor(X, Y))
        )

  • Como você explica os valores de \(\text{cor}(X,Y)\) no item anterior, em comparação com os valores de \(\text{cov}(X,Y)\) nesse outro exercício?

  • Qual a correlação de uma variável aleatória \(X\) com ela mesma?

  • Usando o data frame peso_altura, compute o valor do coeficiente de correlação entre peso e altura usando as fórmulas alternativas.

4.3 Observações importantes

A correlação é linear

(https://commons.wikimedia.org/wiki/File:Pearson_Correlation_Coefficient_and_associated_scatterplots.png)

  • Se \(r > 0\), valores altos de uma variável tendem a corresponder a valores altos da outra variável.

  • Se \(r < 0\), valores altos de uma variável tendem a corresponder a valores baixos da outra variável.

  • Se \(r = 0\), não há correlação linear entre as variáveis.

  • O coeficiente de correlação \(r\) só mede a correlação linear entre duas variáveis.

(https://commons.wikimedia.org/wiki/File:Correlation_examples2.svg)

  • Exemplos de Anscombe, com \(4\) conjuntos de dados com o mesmo valor de \(r\) mas com associações muito diferentes entre as variáveis:

    anscombe
    desenhar <- function(n) {
    
      nomex <- paste0('x', n)
      nomey <- paste0('y', n)
      r <- cor(anscombe[[nomex]], anscombe[[nomey]]) %>% round(2)
    
      anscombe %>% 
        ggplot(aes(.data[[nomex]], .data[[nomey]])) +
          geom_point() +
          geom_smooth(method = 'lm', se = FALSE) +
          labs(
            title = paste('r =', r)
          )
    
    }
    
    
    plots <- map(1:4, desenhar)
    plots[[1]] + plots[[2]] + plots[[3]] + plots[[4]] 

  • Conclusões:

    • O valor de \(r\) só reflete correlação linear.

    • A presença de outliers produz valores não-confiáveis de \(r\).

Correlação \(\neq\) causação

  • Dados coletados em Oldenburg, na Alemanha, nos anos \(1930\), mostrando a correlação entre a quantidade de cegonhas e a população (de pessoas) na cidade:

    df <- read_csv('data/Storks.csv') %>% 
      transmute(
        cegonhas = Storks,
        pessoas = Population
      ) 
    
    r <- cor(df$cegonhas, df$pessoas) %>% round(2)
    
    df %>% 
      ggplot(aes(cegonhas, pessoas)) +
        geom_point() +
        labs(
          title = paste('r =', r)
        )

  • Variável oculta: quantidade de casas com chaminé.

  • Ou coincidência: http://tylervigen.com/spurious-correlations

5 Teste e intervalo de confiança para a correlação

5.1 Exemplo: PIB e CO\(_2\)

  • Em uma amostra de \(10\) países, examinamos o PIB (em trilhões de dólares) e a quantidade de emissões de CO\(_2\) (em milhões de toneladas):

  • Gráfico:

    df %>% 
      ggplot(aes(PIB, emissões)) +
        geom_point() +
        scale_y_continuous(
          'emissões\n(milhões de\ntoneladas)',
          breaks = seq(0, 1200, 200),
          limits = c(0, 1200)
        ) +
        scale_x_continuous(
          'PIB (trilhões US$)',
          breaks = 0:6,
          limits = c(0, 6)
        )

  • O coeficiente de correlação amostral é

    cor(df$PIB, df$emissões)
    ## [1] 0,9115924
  • Como esta é uma amostra, devemos calcular um intervalo de confiança para o coeficiente de correlação populacional.

  • O R faz isto com a função cor.test:

    ct <- cor.test(df$PIB, df$emissões)
    ct
    ## 
    ##  Pearson's product-moment correlation
    ## 
    ## data:  df$PIB and df$emissões
    ## t = 6,272, df = 8, p-value = 0,0002399
    ## alternative hypothesis: true correlation is not equal to 0
    ## 95 percent confidence interval:
    ##  0,6618340 0,9791965
    ## sample estimates:
    ##       cor 
    ## 0,9115924
  • Conclusão: com \(95\%\) de confiança, estimamos que o coeficiente de correlação populacional é capturado pelo intervalo

    \[ [\quad 0{,}66 \quad ;\quad 0{,}98 \quad] \]

5.2 Exercícios

  • Qual é o tipo de teste usado por cor.test?

  • Quais são as hipóteses do teste?

  • Como é calculado o valor da estatística do teste (\(6{,}27\) no exemplo acima)?

  • Como é calculado o valor \(p\) (\(0{,}00\) no exemplo acima)?

  • Implemente uma função em R para fazer o mesmo que cor.test.

6 Transformações

6.1 Exemplo: fotografia

  • Ao configurar uma câmera para tirar uma foto, você deve ajustar a velocidade do obturador e a abertura do diafragma.

  • O fabricante de uma câmera recomenda os seguintes valores:

  • Coeficiente de correlação:

    cor(df$velocidade, df$abertura)
    ## [1] 0,978669
  • Gráfico:

    df %>% 
      ggplot(aes(velocidade, abertura)) +
        geom_point()

  • Vamos transformar os valores de uma das variáveis para tornar a correlação mais linear (menos curva):

    df <- df %>% 
      mutate(quad_abertura = abertura^2)
    df %>% 
      ggplot(aes(velocidade, quad_abertura)) +
        geom_point() +
        labs(y = 'abertura²')

  • A correlação se torna

    cor(df$velocidade, df$quad_abertura)
    ## [1] 0,9983046

6.2 Funções usadas em transformações

  • \(f(x) = x^2\): útil quando os valores originais têm cauda longa à esquerda, ou quando os valores originais têm concavidade para baixo.

  • \(f(x) = \sqrt{x}\): útil para valores que representam contagens.

  • \(f(x) = \log x\): útil para valores positivos, que crescem de acordo com percentagens, como salários, populações etc. Como \(\log 0\) não é definido, você pode precisar acrescentar uma constante \(\varepsilon\) aos valores antes da transformação.

  • \(f(x) = -\frac1{\sqrt{x}}\): o sinal negativo mantém a ordenação dos valores.

  • \(f(x) = -\frac1x\): útil para razões, como km/h. O sinal negativo mantém a ordenação dos valores. Como \(y/0\) não é definido, você pode precisar acrescentar uma constante \(\varepsilon\) aos valores antes da transformação.

6.3 Exercício

  • Aplique as outras transformações da lista acima aos valores da variável abertura. Desenhe gráficos, calcule correlações, e compare os resultados.

6.4 Exemplo: planetas

  • A distância média de um planeta ao Sol, em milhões de km, está correlacionada com a posição do planeta na sequência, mas como?

  • Coeficiente de correlação:

    cor(planetas$posição, planetas$distância)
    ## [1] 0,9102565
  • Gráfico:

    planetas %>% 
      ggplot(aes(posição, distância)) +
        geom_point() +
        scale_x_continuous(breaks = 1:9) +
        labs(
          y = 'distância\n(milhões km)'
        )

  • Vamos transformar os valores das distâncias:

    planetas <- planetas %>% 
      mutate(ldist = log10(distância))
    planetas %>% 
      ggplot(aes(posição, ldist)) +
        geom_point() +
        scale_x_continuous(breaks = 1:9) +
        labs(
          y = 'log distância\n(milhões km)'
        )

  • A correlação se torna

    cor(planetas$posição, planetas$ldist)
    ## [1] 0,990712

6.5 Exercício

  • Aplique as outras transformações da lista acima aos valores da variável distância. Desenhe gráficos, calcule correlações, e compare os resultados.
LS0tCnRpdGxlOiAnQ29ycmVsYcOnw6NvIGVudHJlIGR1YXMgdmFyacOhdmVpcycKc3VidGl0bGU6ICdQcm9iZXN0JwphdXRob3I6ICdmbmF1ZmVsJwplbWFpbDogJ2h0dHBzOi8vZm5hdWZlbC5naXRodWIuaW8vJwpkYXRlOiAnICh2LiBgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVkLyVtLyVZIilgKScKbGFuZzogJ3B0JwpvdXRwdXQ6IHJtZGZvcm1hdDo6Zm5hdWZlbF9ybWRfZm9ybWF0CiMgVG8gaW5zdGFsbCB0aGlzIGZvcm1hdCwgZW50ZXIKIyAgIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZm5hdWZlbC9ybWRmb3JtYXQiKQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQoKb3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsIAogICMgY29sbGFwc2UgPSBUUlVFLAogICMgY2FjaGUgPSBUUlVFLAogIG91dC53aWR0aCA9ICI5MCUiLAogIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogIGZpZy53aWR0aCA9IDcsCiAgZmlnLnNob3cgPSAiaG9sZCIKKQoKIyBTdXByZXNzIGNyYXlvbiBvdXRwdXQKb3B0aW9ucyhjcmF5b24uZW5hYmxlZCA9IEZBTFNFKQoKb3B0aW9ucygKICAjIEF2b2lkIHNjaWVudGlmaWMgbm90YXRpb24KICBzY2lwZW4gPSAxNSwKICAjIFVzZSBhIGNvbW1hIGFzIGRlY2ltYWwgc2VwYXJhdG9yCiAgT3V0RGVjID0gJywnLAogICMgTnVtYmVyIG9mIGRlY2ltYWwgZGlnaXRzIGZvciBudW1iZXJzIHByb2R1Y2VkIGJ5IGlubGluZSBSIGNvZGUKICBmbWRpZ2l0cyA9IDIKKQoKIyBVc2VmdWwgbGlicmFyaWVzCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkobGF0ZXgyZXhwKQpsaWJyYXJ5KGthYmxlRXh0cmEpCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKCiMgRm9yIG5pY2UgZGF0YWZyYW1lIHN1bW1hcmllcwpsaWJyYXJ5KHN1bW1hcnl0b29scykKc3Rfb3B0aW9ucygKICBwbGFpbi5hc2NpaSA9IEZBTFNFLAogIGRmU3VtbWFyeS52YXJudW1iZXJzID0gRkFMU0UsCiAgZGZTdW1tYXJ5LnN0eWxlID0gJ2dyaWQnLAogIGRmU3VtbWFyeS5ncmFwaC5tYWduaWYgPSAuNzUKKQoKIyBUaWR5IQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgVXNlZnVsIGZ1bmN0aW9ucyBwcm92aWRlZCBieSB0aGUgcm1kZm9ybWF0IHBhY2thZ2UKIyAKIyBFeGVjdXRlIAojIAojICAgY2F0KHN5c3RlbS5maWxlKCJybWFya2Rvd24vcmVzb3VyY2VzL2NvbW1vbi5SIiwgcGFja2FnZSA9ICJybWRmb3JtYXQiKSkKIyAKIyB0byBzZWUgdGhlIGxvY2F0aW9uIG9mIHRoZSBmaWxlCnNvdXJjZSgKICBzeXN0ZW0uZmlsZSgKICAgICJybWFya2Rvd24vcmVzb3VyY2VzL2NvbW1vbi5SIiwKICAgIHBhY2thZ2UgPSAicm1kZm9ybWF0IgogICkKKQpgYGAKCgoKIyBFeGVtcGxvOiBhbHR1cmEgZSBwZXNvCgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1UUlVFfQpwZXNvX2FsdHVyYSA8LSByZWFkX2NzdignZGF0YS9IZWlnaHRzX2FuZF9XZWlnaHRzLmNzdicpICU+JSAKICBtdXRhdGUoCiAgICBhbHR1cmEgPSBIZWlnaHQgKiAyLjU0LAogICAgcGVzbyA9IFdlaWdodCAqIDAuNDUKICApCmBgYAoKOjo6IHsucm1kYm94fQoKUGVzc29hcyBtYWlzIGFsdGFzIHRlbmRlbSBhIHNlciBtYWlzIHBlc2FkYXM/CgpUZW1vcyBkYWRvcyBkZSAkYHIgbnJvdyhwZXNvX2FsdHVyYSlgJCBhbHVub3MgZSBhbHVuYXMsIGNvbSBhbHR1cmFzIGVtIGNtIGUgcGVzb3MgZW0ga2c6CgpgYGB7ciBlY2hvPUZBTFNFfQpwZXNvX2FsdHVyYSAlPiUgc2VsZWN0KGFsdHVyYSwgcGVzbykKYGBgCgo6OjoKCiogTyBncsOhZmljbyBkZSBkaXNwZXJzw6NvICgqc2NhdHRlcnBsb3QqKSDDqSBvIGlkZWFsIHBhcmEgdmlzdWFsaXphciBhIGNvcnJlbGHDp8OjbyBlbnRyZSBkdWFzIHZhcmnDoXZlaXMgbnVtw6lyaWNhczoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgZ2dwbG90KGFlcyhhbHR1cmEsIHBlc28pKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdhbHR1cmEgKGNtKScsCiAgICAgICAgICB5ID0gJ3Blc29cbihrZyknCiAgICAgICAgKQogICAgYGBgCgoKIyBDb3JyZWxhw6fDo28gbGluZWFyCgoqIFZhbW9zIHN1YnRyYWlyLCBkZSBjYWRhIHBlc28sIGEgbcOpZGlhIGRvcyBwZXNvczsgdmFtb3Mgc3VidHJhaXIsIGRlIGNhZGEgYWx0dXJhLCBhIG3DqWRpYSBkYXMgYWx0dXJhcy4KCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhIDwtIHBlc29fYWx0dXJhICU+JSAKICAgICAgbXV0YXRlKAogICAgICAgIGFsdHVyYV9kZXN2aW8gPSBhbHR1cmEgLSBtZWFuKGFsdHVyYSksCiAgICAgICAgcGVzb19kZXN2aW8gPSBwZXNvIC0gbWVhbihwZXNvKQogICAgICApCiAgICBgYGAKCiogTyBub3ZvICpzY2F0dGVycGxvdCogdGVtIGEgbWVzbWEgZm9ybWEuIFPDsyBtdWRhbSBhcyBlc2NhbGFzOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFsdHVyYV9kZXN2aW8sIHBlc29fZGVzdmlvKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHggPSAnZGVzdmlvcyBhbHR1cmEgKGNtKScsCiAgICAgICAgICB5ID0gJ2Rlc3Zpb3MgcGVzb1xuKGtnKScKICAgICAgICApCiAgICBgYGAKCiogUXVhbmRvIG8gZGVzdmlvIGRhIGFsdHVyYSBlIG8gZGVzdmlvIGRvIHBlc28gdMOqbSBvIFttZXNtbyBzaW5hbF17LmhsfSwgb3MgdmFsb3JlcyB0w6ptIHVtYSBbYXNzb2NpYcOnw6NvIHBvc2l0aXZhXXsuaGx9OiBvdSBhbHR1cmEgZSBwZXNvIGVzdMOjbyBhbWJvcyBhY2ltYSBkYSBtw6lkaWEsIG91IGFtYm9zIGFiYWl4byBkYSBtw6lkaWEuCgoqIENvbG9yaW5kbyBvcyBwb250b3MgZGUgYWNvcmRvIGNvbSBhIGFzc29jaWHDp8OjbzoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgZ2dwbG90KGFlcyhhbHR1cmFfZGVzdmlvLCBwZXNvX2Rlc3ZpbykpICsKICAgICAgICBnZW9tX3BvaW50KAogICAgICAgICAgZGF0YSA9IC4gJT4lIGZpbHRlcihhbHR1cmFfZGVzdmlvICogcGVzb19kZXN2aW8gPj0gMCksCiAgICAgICAgICBjb2xvciA9ICdibHVlJwogICAgICAgICkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9kZXN2aW8gKiBwZXNvX2Rlc3ZpbyA8IDApLAogICAgICAgICAgY29sb3IgPSAncmVkJwogICAgICAgICkgKwogICAgICAgIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogICAgICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB4ID0gJ2Rlc3Zpb3MgYWx0dXJhIChjbSknLAogICAgICAgICAgeSA9ICdkZXN2aW9zIHBlc29cbihrZyknCiAgICAgICAgKQogICAgYGBgCgojIENvdmFyacOibmNpYQoKKiBFeGFtaW5lIG8gY8OzZGlnbyBhY2ltYTogcGFyYSB2ZXJpZmljYXIgc2Ugb3MgZGVzdmlvcyB0w6ptIG8gbWVzbW8gc2luYWwsIGJhc3RhIHZlcmlmaWNhciBzZSBvIHByb2R1dG8gZG9zIGRlc3Zpb3Mgw6kgcG9zaXRpdm8uCgoqIEUgbWFpczogcXVhbnRvIG1haW9yIG8gcHJvZHV0byBkb3MgZGVzdmlvcywgbWFpcyBhY2ltYSAob3UgbWFpcyBhYmFpeG8pIGRhcyByZXNwZWN0aXZhcyBtw6lkaWFzIG9zIGRlc3Zpb3MgZXN0w6NvLCBbYW8gbWVzbW8gdGVtcG9dey5obH0uIE91IHNlamEsIG1haW9yIGEgYXNzb2NpYcOnw6NvIGVudHJlIGFzIGR1YXMgdmFyacOhdmVpcy4KCiogRXN0YW1vcyBmYWxhbmRvIGRlCgogICQkCiAgXHRleHR7cHJvZHV0byBkb3MgZGVzdmlvc31faSA9IChcdGV4dHthbHR1cmF9X2kgLSBFKFx0ZXh0e2FsdHVyYX0pKSBcY2RvdCAoXHRleHR7cGVzb31faSAtIEUoXHRleHR7cGVzb30pKQogICQkCgoqIFBvZGVtb3MgY2FsY3VsYXIgb3MgcHJvZHV0b3MgZG9zIGRlc3Zpb3MgZSBhY2hhciBhIG3DqWRpYSBkZXN0ZXMgcHJvZHV0b3MuIE8gcmVzdWx0YWRvIHZhaSBzZXIgYSBbY292YXJpw6JuY2lhOl17LmhsfQoKICAkJAogIFx0ZXh0e0Nvdn0oXHRleHR7YWx0dXJhfSwgXHRleHR7cGVzb30pID0gCiAgXGZyYWN7CiAgXHN1bVw7IFsoXHRleHR7YWx0dXJhfV9pIC0gRShcdGV4dHthbHR1cmF9KSkgXGNkb3QgKFx0ZXh0e3Blc299X2kgLSBFKFx0ZXh0e3Blc299KSldCiAgfXtufQogICQkCgoqIE91LCBkZSBmb3JtYSBtYWlzIGNvbXBhY3RhLAoKICAkJAogIFx0ZXh0e0Nvdn0oXHRleHR7YWx0dXJhfSwgXHRleHR7cGVzb30pID0gCiAgRVsoXHRleHR7YWx0dXJhfSAtIEUoXHRleHR7YWx0dXJhfSkpIFxjZG90IChcdGV4dHtwZXNvfSAtIEUoXHRleHR7cGVzb30pKV0KICAkJAoKKiBbTyBSIHVzYSAkbiAtIDEkIG5vIGRlbm9taW5hZG9yLF17LmhsfSBlbSB2ZXogZGUgJG4kLiBJc3RvIHNpZ25pZmljYSBxdWUgbyBSIGNhbGN1bGEgYSBbY292YXJpw6JuY2lhIGFtb3N0cmFsXXsuaGx9LCBxdWUgc2VydmUgY29tbyB1bSBlc3RpbWFkb3IgZGEgY292YXJpw6JuY2lhIHBvcHVsYWNpb25hbC4KCiogQ2FsY3VsYW5kbzoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgbXV0YXRlKHByb2R1dG8gPSBhbHR1cmFfZGVzdmlvICogcGVzb19kZXN2aW8pICU+JSAKICAgICAgc3VtbWFyaXplKGNvdiA9IHN1bShwcm9kdXRvKS8obnJvdyguKSAtIDEpKSAlPiUgCiAgICAgIHB1bGwoY292KQogICAgYGBgCgoKIyMgRW0gUgoKYGBge3J9CmNvdihwZXNvX2FsdHVyYSRhbHR1cmEsIHBlc29fYWx0dXJhJHBlc28pCmBgYAoKIyMgRXhlcmPDrWNpb3MKCiogUXVhbCDDqSBhIHVuaWRhZGUgZGEgY292YXJpw6JuY2lhPyBFeGFtaW5lIGFzIGbDs3JtdWxhcyBhY2ltYSBwYXJhIHJlc3BvbmRlci4KCiogW117I2V4Y292fSBFeGVjdXRlIG8gYmxvY28gYWJhaXhvIGNvbSBkaXZlcnNvcyB2YWxvcmVzIGRlIGBrYCwgaW5jbHVzaXZlIHZhbG9yZXMgbmVnYXRpdm9zLgoKICAgIGBgYHtyfQogICAgayA8LSAxMAogICAgCiAgICBYIDwtIDE6NQogICAgWSA8LSBrICogWAogICAgCiAgICB0aWJibGUoWCwgWSkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKFgsIFkpKSArCiAgICAgICAgZ2VvbV9wb2ludChzaXplID0gMikgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB0aXRsZSA9IHBhc3RlKCdjb3YoWCwgWSkgPScsIGNvdihYLCBZKSkKICAgICAgICApCiAgICBgYGAKCiogUXVhbCBhIGNvdmFyacOibmNpYSBtw6F4aW1hIGVudHJlIGR1YXMgdmFyacOhdmVpcz8gRSBhIG3DrW5pbWE/CgoqIFVzYW5kbyBhcyBwcm9wcmllZGFkZXMgZG8gdmFsb3IgZXNwZXJhZG8sIG1vc3RyZSBxdWUsIHBhcmEgcXVhaXNxdWVyIHZhcmnDoXZlaXMgYWxlYXTDs3JpYXMgJFgkIGUgJFkkCgogICQkCiAgXHRleHR7Q292fShYLFkpIFxxdWFkPVxxdWFkIEVcYmlnWyhYIC0gRShYKSkgXGNkb3QgKFkgLSBFKFkpKVxiaWddIFxxdWFkPVxxdWFkIEUoWFkpIC0gRShYKUUoWSkKICAkJAoKKiBMZW1icmUtc2UgZGUgcXVlIGEgdmFyacOibmNpYSBkZSB1bWEgdmFyacOhdmVsIGFsZWF0w7NyaWEgJFgkIHBvZGUgc2VyIGVzY3JpdGEgY29tbwoKICAkJAogIFx0ZXh0e1Zhcn0oWCkgPSBFKFheMikgLSBbRShYKV1eMgogICQkCgogIEFsZ3VtYSBzZW1lbGhhbsOnYSBlbnRyZSBlc3RhIGV4cHJlc3PDo28gZSBhIGV4cHJlc3PDo28gZG8gaXRlbSBhbnRlcmlvcj8KICAKKiBRdWFsIGEgY292YXJpw6JuY2lhIGRlIHVtYSB2YXJpw6F2ZWwgYWxlYXTDs3JpYSAkWCQgY29tIGVsYSBtZXNtYT8KCiogTyBSIHVzYSAkbiAtIDEkIG5vIGRlbm9taW5hZG9yIGRhIGNvdmFyacOibmNpYS4gTW9zdHJlIHF1ZSBkZWZpbmlyIGEgY292YXJpw6JuY2lhIGNvbW8gCiAgJCQKICBcdGV4dHtDb3Z9KFgsIFkpID0gXGZyYWN7MX17biAtIDF9IFxjZG90IFxzdW1fe2kgPSAxfV5uIFxiaWdbKHhfaSAtIEUoWCkpIFxjZG90ICh5X2kgLSBFKFkpKSBcYmlnXQogICQkCgogIMOpIGVxdWl2YWxlbnRlIGEKICAKICAkJAogIFx0ZXh0e0Nvdn0oWCwgWSkgPSBcZnJhY3tufXtuLTF9IFxjZG90IFxiaWdbIEUoWFkpIC0gRShYKUUoWSkgXGJpZ10KICAkJAoKCiogVXNhbmRvIG8gKmRhdGEgZnJhbWUqIGBwZXNvX2FsdHVyYWAsIGNvbXB1dGUgbyB2YWxvciBkYSBleHByZXNzw6NvIGFjaW1hIGUgY29tcGFyZSBjb20gbyByZXN1bHRhZG8gZGUgYGNvdihwZXNvX2FsdHVyYSRhbHR1cmEsIHBlc29fYWx0dXJhJHBlc28pYC4KCgojIENvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBsaW5lYXIKCiogVmFtb3MgcGFkcm9uaXphciBhcyBhbHR1cmFzIGUgcGVzb3MgKHN1YnRyYWlyIGEgbcOpZGlhIGUgZGl2aWRpciBwZWxvIGRlc3ZpbyBwYWRyw6NvKToKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhIDwtIHBlc29fYWx0dXJhICU+JSAKICAgICAgbXV0YXRlKAogICAgICAgIGFsdHVyYV9wYWRyb25pemFkYSA9IHNjYWxlKGFsdHVyYSksCiAgICAgICAgcGVzb19wYWRyb25pemFkbyA9IHNjYWxlKHBlc28pCiAgICAgICkKICAgIGBgYAoKKiBFIGNvbnN0cnVpciBvICpzY2F0dGVycGxvdCo6CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIGdncGxvdChhZXMoYWx0dXJhX3BhZHJvbml6YWRhLCBwZXNvX3BhZHJvbml6YWRvKSkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9wYWRyb25pemFkYSAqIHBlc29fcGFkcm9uaXphZG8gPj0gMCksCiAgICAgICAgICBjb2xvciA9ICdibHVlJwogICAgICAgICkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9wYWRyb25pemFkYSAqIHBlc29fcGFkcm9uaXphZG8gPCAwKSwKICAgICAgICAgIGNvbG9yID0gJ3JlZCcKICAgICAgICApICsKICAgICAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsKICAgICAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdhbHR1cmEgcGFkcm9uaXphZGEnLAogICAgICAgICAgeSA9ICdwZXNvXG5wYWRyb25pemFkbycKICAgICAgICApCiAgICBgYGAKCiogT3Ugc2VqYSwgYWdvcmEgYXMgdmFyacOhdmVpcyBzw6NvCgogICQkCiAgWl9BID0gXGZyYWN7XHRleHR7YWx0dXJhfSAtIEUoXHRleHR7YWx0dXJhfSl9e0RQKFx0ZXh0e2FsdHVyYX0pfQogICQkCgogIGUKICAKICAkJAogIFpfUCA9IFxmcmFje1x0ZXh0e3Blc299IC0gRShcdGV4dHtwZXNvfSl9e0RQKFx0ZXh0e3Blc299KX0KICAkJAoKKiBBIGNvdmFyacOibmNpYSBlbnRyZSBlbGFzIMOpCgogICAgYGBge3J9CiAgICBjb3YocGVzb19hbHR1cmEkcGVzb19wYWRyb25pemFkbywgcGVzb19hbHR1cmEkYWx0dXJhX3BhZHJvbml6YWRhKQogICAgYGBgCgoqIFF1YW5kbyBwYWRyb25pemFtb3MgYXMgZHVhcyB2YXJpw6F2ZWlzLCBhIGNvdmFyacOibmNpYSBlbnRyZSBlbGFzIHNlIGNoYW1hIFtjb3JyZWxhw6fDo29dey5obH0uCgoqIE8gW2NvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBhbW9zdHJhbF17LmhsfSDDqSBlc3RlIHZhbG9yLgoKICAkJAogIHIgPSBcZnJhY3tcc3VtIHhfaSB5X2l9e24gLSAxfQogICQkCgogIG9uZGUgWyRYJCBlICRZJCBzw6NvIHZhcmnDoXZlaXMgcGFkcm9uaXphZGFzXXsuaGx9LgogIAoqIEFsZ3VucyBsaXZyb3MgY2hhbWFtIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGRlICRccmhvJCBlbSB2ZXogZGUgJHIkLgoKKiBPIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBbc2VtcHJlIGVzdMOhIGVudHJlICQtMSQgZSAkMSRdey5obH0sIGluY2x1c2l2ZS4KCiogRsOzcm11bGFzIGFsdGVybmF0aXZhcyBwYXJhIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGVudHJlICRYJCBlICRZJCAobsOjbyBuZWNlc3NhcmlhbWVudGUgcGFkcm9uaXphZGFzKToKCiAgJCQKICByID0gXGZyYWN7XHRleHR7Q292fShYLCBZKX17RFBfWCBcY2RvdCBEUF9ZfQogICQkCiAgCiAgZQoKICAkJAogIHIgPSBcZnJhY3sKICAgIFxzdW0oeF9pIC0gRShYKSlcY2RvdCh5X2kgLSBFKFkpKQogIH17CiAgICBcc3FydHtcc3VtKHhfaSAtIEUoWCkpXjJcY2RvdCh5X2kgLSBFKFkpKV4yfQogIH0KICAkJAoKCiMjIEVtIFIKCmBgYHtyfQpjb3IocGVzb19hbHR1cmEkcGVzbywgcGVzb19hbHR1cmEkYWx0dXJhKQpgYGAKCiMjIEV4ZXJjw61jaW9zCgoqIFF1YWwgYSB1bmlkYWRlIGRvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8Ojbz8KCiogRXhlY3V0ZSBvIGJsb2NvIGFiYWl4byBjb20gZGl2ZXJzb3MgdmFsb3JlcyBkZSBga2AsIGluY2x1c2l2ZSB2YWxvcmVzIG5lZ2F0aXZvcy4KCiAgICBgYGB7cn0KICAgIGsgPC0gMTAKICAgIAogICAgWCA8LSAxOjUKICAgIFkgPC0gayAqIFgKICAgIAogICAgdGliYmxlKFgsIFkpICU+JSAKICAgICAgZ2dwbG90KGFlcyhYLCBZKSkgKwogICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICAgICAgICBsYWJzKAogICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnY29yKFgsIFkpID0nLCBjb3IoWCwgWSkpCiAgICAgICAgKQogICAgYGBgCgoqIENvbW8gdm9jw6ogZXhwbGljYSBvcyB2YWxvcmVzIGRlICRcdGV4dHtjb3J9KFgsWSkkIG5vIGl0ZW0gYW50ZXJpb3IsIGVtIGNvbXBhcmHDp8OjbyBjb20gb3MgdmFsb3JlcyBkZSAkXHRleHR7Y292fShYLFkpJCBbbmVzc2Ugb3V0cm8gZXhlcmPDrWNpb10oI2V4Y292KT8KCiogUXVhbCBhIGNvcnJlbGHDp8OjbyBkZSB1bWEgdmFyacOhdmVsIGFsZWF0w7NyaWEgJFgkIGNvbSBlbGEgbWVzbWE/CgoqIFVzYW5kbyBvICpkYXRhIGZyYW1lKiBgcGVzb19hbHR1cmFgLCBjb21wdXRlIG8gdmFsb3IgZG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGVudHJlIHBlc28gZSBhbHR1cmEgdXNhbmRvIGFzIGbDs3JtdWxhcyBhbHRlcm5hdGl2YXMuCgoKIyMgT2JzZXJ2YcOnw7VlcyBpbXBvcnRhbnRlcwoKIyMjIEEgY29ycmVsYcOnw6NvIMOpICpsaW5lYXIqIHstfQoKIVtdKGltYWdlcy9QZWFyc29uX0NvcnJlbGF0aW9uX0NvZWZmaWNpZW50X2FuZF9hc3NvY2lhdGVkX3NjYXR0ZXJwbG90cy5wbmcpeyBzdHlsZT0id2lkdGg6IDkwJTsiIC5jZW50ZXIgfQoKOjo6e3N0eWxlPSJ0ZXh0LWFsaWduOiByaWdodDsifQooaHR0cHM6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOlBlYXJzb25fQ29ycmVsYXRpb25fQ29lZmZpY2llbnRfYW5kX2Fzc29jaWF0ZWRfc2NhdHRlcnBsb3RzLnBuZykKOjo6CgoqIFNlICRyID4gMCQsIHZhbG9yZXMgW2FsdG9zXXsuaGx9IGRlIHVtYSB2YXJpw6F2ZWwgdGVuZGVtIGEgY29ycmVzcG9uZGVyIGEgdmFsb3JlcyBbYWx0b3Ndey5obH0gZGEgb3V0cmEgdmFyacOhdmVsLgoKKiBTZSAkciA8IDAkLCB2YWxvcmVzIFthbHRvc117LmhsfSBkZSB1bWEgdmFyacOhdmVsIHRlbmRlbSBhIGNvcnJlc3BvbmRlciBhIHZhbG9yZXMgW2JhaXhvc117LmhsfSBkYSBvdXRyYSB2YXJpw6F2ZWwuCgoqIFNlICRyID0gMCQsIG7Do28gaMOhIGNvcnJlbGHDp8OjbyBbbGluZWFyXXsuaGx9IGVudHJlIGFzIHZhcmnDoXZlaXMuCgoqIE8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvICRyJCBzw7MgbWVkZSBhIGNvcnJlbGHDp8OjbyBbbGluZWFyXXsuaGx9IGVudHJlIGR1YXMgdmFyacOhdmVpcy4KCiAgIVtdKGltYWdlcy9Db3JyZWxhdGlvbl9leGFtcGxlczIuc3ZnKXsgc3R5bGU9IndpZHRoOiA5MCU7IiAuY2VudGVyIH0KCjo6OntzdHlsZT0idGV4dC1hbGlnbjogcmlnaHQ7In0KKGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpDb3JyZWxhdGlvbl9leGFtcGxlczIuc3ZnKQo6OjoKCiogW0V4ZW1wbG9zIGRlIEFuc2NvbWJlXShodHRwczovL3B0Lndpa2lwZWRpYS5vcmcvd2lraS9RdWFydGV0b19kZV9BbnNjb21iZSksIGNvbSAkNCQgY29uanVudG9zIGRlIGRhZG9zIGNvbSBbbyBtZXNtbyB2YWxvciBkZSAkciRdey5obH0gbWFzIGNvbSBhc3NvY2lhw6fDtWVzIFttdWl0byBkaWZlcmVudGVzXXsuaGx9IGVudHJlIGFzIHZhcmnDoXZlaXM6CgogIGBgYHtyfQogIGFuc2NvbWJlCiAgYGBgCgogICAgYGBge3IgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRX0KICAgIGRlc2VuaGFyIDwtIGZ1bmN0aW9uKG4pIHsKICAgICAgCiAgICAgIG5vbWV4IDwtIHBhc3RlMCgneCcsIG4pCiAgICAgIG5vbWV5IDwtIHBhc3RlMCgneScsIG4pCiAgICAgIHIgPC0gY29yKGFuc2NvbWJlW1tub21leF1dLCBhbnNjb21iZVtbbm9tZXldXSkgJT4lIHJvdW5kKDIpCiAgICAgIAogICAgICBhbnNjb21iZSAlPiUgCiAgICAgICAgZ2dwbG90KGFlcyguZGF0YVtbbm9tZXhdXSwgLmRhdGFbW25vbWV5XV0pKSArCiAgICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGQUxTRSkgKwogICAgICAgICAgbGFicygKICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnciA9JywgcikKICAgICAgICAgICkKICAgICAgCiAgICB9CiAgICAgIAogICAgICAKICAgIHBsb3RzIDwtIG1hcCgxOjQsIGRlc2VuaGFyKQogICAgcGxvdHNbWzFdXSArIHBsb3RzW1syXV0gKyBwbG90c1tbM11dICsgcGxvdHNbWzRdXSAKICAgIGBgYAoKKiBDb25jbHVzw7VlczoKCiAgKiBPIHZhbG9yIGRlICRyJCBzw7MgcmVmbGV0ZSBjb3JyZWxhw6fDo28gW2xpbmVhcl17LmhsfS4KICAKICAqIEEgcHJlc2Vuw6dhIGRlIFsqb3V0bGllcnMqXXsuaGx9IHByb2R1eiB2YWxvcmVzIG7Do28tY29uZmnDoXZlaXMgZGUgJHIkLgogIAogIAojIyMgQ29ycmVsYcOnw6NvICRcbmVxJCBjYXVzYcOnw6NvIHstfQoKKiBEYWRvcyBjb2xldGFkb3MgZW0gT2xkZW5idXJnLCBuYSBBbGVtYW5oYSwgbm9zIGFub3MgJDE5MzAkLCBtb3N0cmFuZG8gYSBjb3JyZWxhw6fDo28gZW50cmUgYSBxdWFudGlkYWRlIGRlIGNlZ29uaGFzIGUgYSBwb3B1bGHDp8OjbyAoZGUgcGVzc29hcykgbmEgY2lkYWRlOgoKICAgIGBgYHtyIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPVRSVUV9CiAgICBkZiA8LSByZWFkX2NzdignZGF0YS9TdG9ya3MuY3N2JykgJT4lIAogICAgICB0cmFuc211dGUoCiAgICAgICAgY2Vnb25oYXMgPSBTdG9ya3MsCiAgICAgICAgcGVzc29hcyA9IFBvcHVsYXRpb24KICAgICAgKSAKICAgIAogICAgciA8LSBjb3IoZGYkY2Vnb25oYXMsIGRmJHBlc3NvYXMpICU+JSByb3VuZCgyKQogICAgCiAgICBkZiAlPiUgCiAgICAgIGdncGxvdChhZXMoY2Vnb25oYXMsIHBlc3NvYXMpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBsYWJzKAogICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnciA9JywgcikKICAgICAgICApCiAgICAKICAgIGBgYAoKKiBWYXJpw6F2ZWwgb2N1bHRhOiBxdWFudGlkYWRlIGRlIGNhc2FzIGNvbSBjaGFtaW7DqS4KCiogT3UgY29pbmNpZMOqbmNpYTogaHR0cDovL3R5bGVydmlnZW4uY29tL3NwdXJpb3VzLWNvcnJlbGF0aW9ucwoKCiMgVGVzdGUgZSBpbnRlcnZhbG8gZGUgY29uZmlhbsOnYSBwYXJhIGEgY29ycmVsYcOnw6NvCgojIyBFeGVtcGxvOiBQSUIgZSBDTyRfMiQKCiogRW0gdW1hIGFtb3N0cmEgZGUgJDEwJCBwYcOtc2VzLCBleGFtaW5hbW9zIG8gUElCIChlbSB0cmlsaMO1ZXMgZGUgZMOzbGFyZXMpIGUgYSBxdWFudGlkYWRlIGRlIGVtaXNzw7VlcyBkZSBDTyRfMiQgKGVtIG1pbGjDtWVzIGRlIHRvbmVsYWRhcyk6CgogIGBgYHtyIGVjaG89RkFMU0V9CiAgZGYgPC0gdGliYmxlKAogICAgUElCID0gYygxLjcsIDEuMiwgMi41LCAyLjgsIDMuNiwgMi4yLCAwLjgsIDEuNSwgMi40LCA1LjkpLAogICAgZW1pc3PDtWVzID0gYygKICAgICAgNTUyLjYsIDQ2Mi4zLCA0NzUuNCwgMzc0LjMsIDc0OC41LCA0MDAuOSwgMjUzLjAsIDMxOC42LCA0OTYuOCwgMTE4MC42CiAgICApCiAgKQogIAogIGRmCiAgYGBgCgoqIEdyw6FmaWNvOgoKICAgIGBgYHtyfQogICAgZGYgJT4lIAogICAgICBnZ3Bsb3QoYWVzKFBJQiwgZW1pc3PDtWVzKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKAogICAgICAgICAgJ2VtaXNzw7Vlc1xuKG1pbGjDtWVzIGRlXG50b25lbGFkYXMpJywKICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMjAwLCAyMDApLAogICAgICAgICAgbGltaXRzID0gYygwLCAxMjAwKQogICAgICAgICkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cygKICAgICAgICAgICdQSUIgKHRyaWxow7VlcyBVUyQpJywKICAgICAgICAgIGJyZWFrcyA9IDA6NiwKICAgICAgICAgIGxpbWl0cyA9IGMoMCwgNikKICAgICAgICApCiAgICBgYGAKCiogTyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gW2Ftb3N0cmFsXXsuaGx9IMOpCgogICAgYGBge3J9CiAgICBjb3IoZGYkUElCLCBkZiRlbWlzc8O1ZXMpCiAgICBgYGAKCiogQ29tbyBlc3RhIMOpIHVtYSBhbW9zdHJhLCBkZXZlbW9zIGNhbGN1bGFyIHVtIGludGVydmFsbyBkZSBjb25maWFuw6dhIHBhcmEgbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gW3BvcHVsYWNpb25hbF17LmhsfS4KCiogTyBSIGZheiBpc3RvIGNvbSBhIGZ1bsOnw6NvIGBjb3IudGVzdGA6CgogICAgYGBge3J9CiAgICBjdCA8LSBjb3IudGVzdChkZiRQSUIsIGRmJGVtaXNzw7VlcykKICAgIGN0CiAgICBgYGAKCiogQ29uY2x1c8OjbzogY29tICQ5NVwlJCBkZSBjb25maWFuw6dhLCBlc3RpbWFtb3MgcXVlIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIFtwb3B1bGFjaW9uYWxdey5obH0gw6kgY2FwdHVyYWRvIHBlbG8gaW50ZXJ2YWxvCgogICQkCiAgW1xxdWFkIGByIGN0JGNvbmYuaW50WzFdYCBccXVhZCA7XHF1YWQgIGByIGN0JGNvbmYuaW50WzJdYCBccXVhZF0KICAkJAoKIyMgRXhlcmPDrWNpb3MKCiogUXVhbCDDqSBvIHRpcG8gZGUgdGVzdGUgdXNhZG8gcG9yIGBjb3IudGVzdGA/CgoqIFF1YWlzIHPDo28gYXMgaGlww7N0ZXNlcyBkbyB0ZXN0ZT8KCiogQ29tbyDDqSBjYWxjdWxhZG8gbyB2YWxvciBkYSBlc3RhdMOtc3RpY2EgZG8gdGVzdGUgKCRgciBjdCRzdGF0aXN0aWMgJT4lIHJvdW5kKDQpYCQgbm8gZXhlbXBsbyBhY2ltYSk/CgoqIENvbW8gw6kgY2FsY3VsYWRvIG8gdmFsb3IgJHAkICgkYHIgY3QkcC52YWx1ZSAlPiUgcm91bmQoNylgJCBubyBleGVtcGxvIGFjaW1hKT8KCiogSW1wbGVtZW50ZSB1bWEgZnVuw6fDo28gZW0gUiBwYXJhIGZhemVyIG8gbWVzbW8gcXVlIGBjb3IudGVzdGAuCgoKIyBUcmFuc2Zvcm1hw6fDtWVzCgojIyBFeGVtcGxvOiBmb3RvZ3JhZmlhCgoqIEFvIGNvbmZpZ3VyYXIgdW1hIGPDom1lcmEgcGFyYSB0aXJhciB1bWEgZm90bywgdm9jw6ogZGV2ZSBhanVzdGFyIGEgdmVsb2NpZGFkZSBkbyBvYnR1cmFkb3IgZSBhIGFiZXJ0dXJhIGRvIGRpYWZyYWdtYS4KCiogTyBmYWJyaWNhbnRlIGRlIHVtYSBjw6JtZXJhIHJlY29tZW5kYSBvcyBzZWd1aW50ZXMgdmFsb3JlczoKCiAgYGBge3IgZWNobz1GQUxTRX0KICBkZiA8LSB0aWJibGUoCiAgICB2ZWxvY2lkYWRlID0gYygKICAgICAgMSAvIDEwMDAsCiAgICAgIDEgLyA1MDAsCiAgICAgIDEgLyAyNTAsCiAgICAgIDEgLyAxMjUsCiAgICAgIDEgLyA2MCwKICAgICAgMSAvIDMwLAogICAgICAxIC8gMTUsCiAgICAgIDEgLyA4CiAgICApLAogICAgYWJlcnR1cmEgPSBjKDIuOCwgNCwgNS42LCA4LCAxMSwgMTYsIDIyLCAzMikKICApCiAgCiAgZGYKICBgYGAKCiogQ29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvOgoKICAgIGBgYHtyfQogICAgY29yKGRmJHZlbG9jaWRhZGUsIGRmJGFiZXJ0dXJhKQogICAgYGBgCgoqIEdyw6FmaWNvOgoKICAgIGBgYHtyfQogICAgZGYgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHZlbG9jaWRhZGUsIGFiZXJ0dXJhKSkgKwogICAgICAgIGdlb21fcG9pbnQoKQogICAgYGBgCgoqIFZhbW9zIHRyYW5zZm9ybWFyIG9zIHZhbG9yZXMgZGUgdW1hIGRhcyB2YXJpw6F2ZWlzIHBhcmEgdG9ybmFyIGEgY29ycmVsYcOnw6NvIG1haXMgbGluZWFyIChtZW5vcyBjdXJ2YSk6CgogICAgYGBge3J9CiAgICBkZiA8LSBkZiAlPiUgCiAgICAgIG11dGF0ZShxdWFkX2FiZXJ0dXJhID0gYWJlcnR1cmFeMikKICAgIGBgYAogICAgCiAgICBgYGB7cn0KICAgIGRmICU+JSAKICAgICAgZ2dwbG90KGFlcyh2ZWxvY2lkYWRlLCBxdWFkX2FiZXJ0dXJhKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgbGFicyh5ID0gJ2FiZXJ0dXJhwrInKQogICAgYGBgCgoqIEEgY29ycmVsYcOnw6NvIHNlIHRvcm5hCgogICAgYGBge3J9CiAgICBjb3IoZGYkdmVsb2NpZGFkZSwgZGYkcXVhZF9hYmVydHVyYSkKICAgIGBgYAoKIyMgRnVuw6fDtWVzIHVzYWRhcyBlbSB0cmFuc2Zvcm1hw6fDtWVzCgoqICRmKHgpID0geF4yJDogw7p0aWwgcXVhbmRvIG9zIHZhbG9yZXMgb3JpZ2luYWlzIHTDqm0gY2F1ZGEgbG9uZ2Egw6AgZXNxdWVyZGEsIG91IHF1YW5kbyBvcyB2YWxvcmVzIG9yaWdpbmFpcyB0w6ptIGNvbmNhdmlkYWRlIHBhcmEgYmFpeG8uCgoqICRmKHgpID0gXHNxcnR7eH0kOiDDunRpbCBwYXJhIHZhbG9yZXMgcXVlIHJlcHJlc2VudGFtIGNvbnRhZ2Vucy4KCiogJGYoeCkgPSBcbG9nIHgkOiDDunRpbCBwYXJhIHZhbG9yZXMgcG9zaXRpdm9zLCBxdWUgY3Jlc2NlbSBkZSBhY29yZG8gY29tIHBlcmNlbnRhZ2VucywgY29tbyBzYWzDoXJpb3MsIHBvcHVsYcOnw7VlcyBldGMuIENvbW8gJFxsb2cgMCQgbsOjbyDDqSBkZWZpbmlkbywgdm9jw6ogcG9kZSBwcmVjaXNhciBhY3Jlc2NlbnRhciB1bWEgY29uc3RhbnRlICRcdmFyZXBzaWxvbiQgYW9zIHZhbG9yZXMgYW50ZXMgZGEgdHJhbnNmb3JtYcOnw6NvLgoKKiAkZih4KSA9IC1cZnJhYzF7XHNxcnR7eH19JDogbyBzaW5hbCBuZWdhdGl2byBtYW50w6ltIGEgb3JkZW5hw6fDo28gZG9zIHZhbG9yZXMuCgoqICRmKHgpID0gLVxmcmFjMXgkOiDDunRpbCBwYXJhIHJhesO1ZXMsIGNvbW8ga20vaC4gTyBzaW5hbCBuZWdhdGl2byBtYW50w6ltIGEgb3JkZW5hw6fDo28gZG9zIHZhbG9yZXMuIENvbW8gJHkvMCQgbsOjbyDDqSBkZWZpbmlkbywgdm9jw6ogcG9kZSBwcmVjaXNhciBhY3Jlc2NlbnRhciB1bWEgY29uc3RhbnRlICRcdmFyZXBzaWxvbiQgYW9zIHZhbG9yZXMgYW50ZXMgZGEgdHJhbnNmb3JtYcOnw6NvLgoKCiMjIEV4ZXJjw61jaW8KCiogQXBsaXF1ZSBhcyBvdXRyYXMgdHJhbnNmb3JtYcOnw7VlcyBkYSBsaXN0YSBhY2ltYSBhb3MgdmFsb3JlcyBkYSB2YXJpw6F2ZWwgYGFiZXJ0dXJhYC4gRGVzZW5oZSBncsOhZmljb3MsIGNhbGN1bGUgY29ycmVsYcOnw7VlcywgZSBjb21wYXJlIG9zIHJlc3VsdGFkb3MuCgoKIyMgRXhlbXBsbzogcGxhbmV0YXMKCiogQSBkaXN0w6JuY2lhIG3DqWRpYSBkZSB1bSBwbGFuZXRhIGFvIFNvbCwgZW0gbWlsaMO1ZXMgZGUga20sIGVzdMOhIGNvcnJlbGFjaW9uYWRhIGNvbSBhIHBvc2nDp8OjbyBkbyBwbGFuZXRhIG5hIHNlcXXDqm5jaWEsIG1hcyBjb21vPwoKICBgYGB7ciBlY2hvPUZBTFNFfQogIHBsYW5ldGFzIDwtIHRyaWJibGUoCiAgICB+bm9tZSwgfnBvc2nDp8OjbywgfmRpc3TDom5jaWEsCiAgICAnTWVyY8O6cmlvJywgMSwgMzYsCiAgICAnVsOqbnVzJywgMiwgNjcsCiAgICAnVGVycmEnLCAzLCA5MywKICAgICdNYXJ0ZScsIDQsIDE0MiwKICAgICdKw7pwaXRlcicsIDUsIDQ4NCwKICAgICdTYXR1cm5vJywgNiwgODg3LAogICAgJ1VyYW5vJywgNywgMTc4NCwKICAgICdOZXR1bm8nLCA4LCAyNzk2LAogICAgJ1BsdXTDo28nLCA5LCAzNjY2CiAgKSAlPiUgCiAgICBtdXRhdGUoCiAgICAgIGRpc3TDom5jaWEgPSBkaXN0w6JuY2lhICogMS42CiAgICApCiAgCiAgcGxhbmV0YXMKICBgYGAKCiogQ29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvOgoKICAgIGBgYHtyfQogICAgY29yKHBsYW5ldGFzJHBvc2nDp8OjbywgcGxhbmV0YXMkZGlzdMOibmNpYSkKICAgIGBgYAoKKiBHcsOhZmljbzoKCiAgICBgYGB7cn0KICAgIHBsYW5ldGFzICU+JSAKICAgICAgZ2dwbG90KGFlcyhwb3Npw6fDo28sIGRpc3TDom5jaWEpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo5KSArCiAgICAgICAgbGFicygKICAgICAgICAgIHkgPSAnZGlzdMOibmNpYVxuKG1pbGjDtWVzIGttKScKICAgICAgICApCiAgICBgYGAKCiogVmFtb3MgdHJhbnNmb3JtYXIgb3MgdmFsb3JlcyBkYXMgZGlzdMOibmNpYXM6CgogICAgYGBge3J9CiAgICBwbGFuZXRhcyA8LSBwbGFuZXRhcyAlPiUgCiAgICAgIG11dGF0ZShsZGlzdCA9IGxvZzEwKGRpc3TDom5jaWEpKQogICAgYGBgCiAgICAKICAgIGBgYHtyfQogICAgcGxhbmV0YXMgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHBvc2nDp8OjbywgbGRpc3QpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo5KSArCiAgICAgICAgbGFicygKICAgICAgICAgIHkgPSAnbG9nIGRpc3TDom5jaWFcbihtaWxow7VlcyBrbSknCiAgICAgICAgKQogICAgYGBgCgoqIEEgY29ycmVsYcOnw6NvIHNlIHRvcm5hCgogICAgYGBge3J9CiAgICBjb3IocGxhbmV0YXMkcG9zacOnw6NvLCBwbGFuZXRhcyRsZGlzdCkKICAgIGBgYAoKCiMjIEV4ZXJjw61jaW8KCiogQXBsaXF1ZSBhcyBvdXRyYXMgdHJhbnNmb3JtYcOnw7VlcyBkYSBsaXN0YSBhY2ltYSBhb3MgdmFsb3JlcyBkYSB2YXJpw6F2ZWwgYGRpc3TDom5jaWFgLiBEZXNlbmhlIGdyw6FmaWNvcywgY2FsY3VsZSBjb3JyZWxhw6fDtWVzLCBlIGNvbXBhcmUgb3MgcmVzdWx0YWRvcy4KCgoKCjxkaXYgc3R5bGU9J2hlaWdodDogMTAwMHB4Jz48L2Rpdj4K